package com.spider.runnable;

import com.spider.pipeline.DetailPagePipeline;
import com.spider.processor.DetailPageProcessor;
import com.spider.entites.Filed;
import com.spider.entites.Task;
import com.spider.entites.TaskSetting;
import com.spider.utils.JsonUtils;
import com.spider.utils.StringUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.pipeline.Pipeline;
import us.codecraft.webmagic.processor.PageProcessor;

import javax.annotation.Resource;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author panglin
 * @date 2020/12/1 19:33
 */
@Component
@Scope("prototype")
public class DetailPageRunnable implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(DetailPageRunnable.class);

    @Resource
    private DetailPagePipeline pipeline;

    @Resource
    private DetailPageProcessor processor;

    private Task task;

    private QueryRunner queryRunner;

    private TaskSetting setting;


    public Runnable initTask(Task task, QueryRunner queryRunner, TaskSetting setting) {
        this.task = task;
        this.queryRunner = queryRunner;
        this.setting = setting;
        return this;
    }

    @Override
    public void run() {
        String detailJson = this.task.getDetailFiled();
        String flag = detailJson.substring(1, detailJson.length() - 1);
        if (StringUtils.isEmpty(flag)) {
            this.finishTask();
        } else {
            List<Filed> filedList = JsonUtils.transformJsonToListFiled(detailJson);
            List<Map<String, String>> urls = this.queryDetailPageUrls();
            if (CollectionUtils.isEmpty(urls)) {
                this.finishTask();
                return;
            }
            this.executeTask(filedList, urls);
        }
    }

    /**
     * 执行详情页爬取任务（同步）
     *
     * @param filedList
     * @param urls
     */
    private void executeTask(List<Filed> filedList, List<Map<String, String>> urls) {
        urls.forEach(url -> this.detailStarter(this.processor.initPageProcessor(this.setting, filedList), this.pipeline.initPipeline(filedList, this.task.getTbName(), url.get("id"), this.queryRunner), this.checkUrl(url), this.setting.getThreadNum()));
        this.finishTask();
    }

    /**
     * 详情页url预检
     *
     * @param url
     * @return
     */
    private String checkUrl(Map<String, String> url) {
        String flag = "http";
        if (url.get("url").startsWith(flag)) {
            return url.get("url");
        }
        String[] urls = this.task.getIndexPage().split("//");
        String suffix = urls[1].substring(0, urls[1].indexOf("/"));
        url.put("url", urls[0] + "//" + suffix + url.get("url"));
        return url.get("url");
    }

    /**
     * 任务完成更新任务状态
     */
    private void finishTask() {
        try {
            this.queryRunner.update("update tb_tasks set runStatus = 2,salveStatus = 2,endTime=? where id=?", new Timestamp(System.currentTimeMillis()), this.task.getId());
        } catch (SQLException throwable) {
            throwable.printStackTrace();
        }
        LOGGER.info("详情页任务执行完毕,任务名称:{}", this.task.getTaskName());
    }

    /**
     * 获取列表页的url
     *
     * @return
     */
    private List<Map<String, String>> queryDetailPageUrls() {
        try {
            return this.queryRunner.query("select id,pageUrl from " + this.task.getTbName() + " where pageUrl is not null limit 0,?", rs -> {
                List<Map<String, String>> list = new ArrayList<>();
                while (rs.next()) {
                    if (StringUtils.isNotEmpty(rs.getString(2))) {
                        Map<String, String> map = new HashMap();
                        map.put("id", rs.getString(1));
                        map.put("url", rs.getString(2));
                        list.add(map);
                    }
                }
                return list;
            }, this.task.getSpiderNum());
        } catch (SQLException throwable) {
            throwable.printStackTrace();
            return null;
        }
    }

    /**
     * 同步启动
     *
     * @param processor
     * @param pipeline
     * @param url
     * @param threadNum
     */
    private void detailStarter(PageProcessor processor, Pipeline pipeline, String url, Integer threadNum) {
        Spider.create(processor)
                .addUrl(url)
                .addPipeline(pipeline)
                .thread(threadNum)
                .run();
    }
}
